import axios from 'axios'
import { isObject } from './utils/index.js'
import CancelToken from './CancelToken.js'
import Cache from './Cache.js'


// 拦截器处理（封装为数组形式[successF, errorF]） 返回值：数组 || undefined
function InterceptorsHandle(interceptors) {
  if(typeof interceptors === 'function') {
    return [interceptors, err => Promise.reject(err)]
  }
  return interceptors
}

export function to(promise, errorExt) {
  return promise
    .then(function (data) { return [null, data]; })
    .catch(function (err) {
      if (errorExt) {
          Object.assign(err, errorExt);
      }
      return [err, undefined];
    });
}

// 创建强化版axios实例
export default function createEnhanceAxiosInstance(config) {
  let AxiosProxy
  let {
    axiosInitConfig = {}, // 初始化Axios配置对象（用于创建axios实例的配置）

    requestInterceptors, // 请求拦截器（type = function | [] ｜ undefined）
    responseInterceptors, // 响应拦截器

    open_cancel_model = true, // 开启取消重复请求模块（保留最新一次请求，cancelToken）
    cancelWhiteList = [],  // 过滤 白名单（允许重复请求的接口） 精确到url路径即可 针对get请求即可（post请求走防抖，校验post请求消耗空间多，要把请求体做MD5处理）

    // open_cache_model = false, // 开启缓存接口模块(cacheList有内容就说明开启了，不需要冗余标识)
    cacheList = [], // 需要缓存的接口名称 精确到查询参数 针对get请求

    open_async_optimize = false, // 开启异步优化（使用to封装）
    async_optimize_method_list = ['get', 'post', 'delete', 'put', 'all', 'spread'], // 开启异步优化的方法名(开启异步优化才有用)

    open_link_stop = false, // 开启Promise链式中断（Promise函数包装）

    re_try_times = 0, // 重试次数
  } = config
  let $axios = axios.create({
    baseURL: '',
    responseType: 'json',
    withCredentials: true,
    ...axiosInitConfig
  })
  let $ct = null
  let $cache = null
  open_cancel_model && ($ct = new CancelToken(cancelWhiteList)) // 维护一个CancelToken实例
  cacheList.length && ($cache = new Cache(cacheList)) // 维护一个Cache实例
  
  requestInterceptors = InterceptorsHandle(requestInterceptors) // undefined | [function, function]
  responseInterceptors = InterceptorsHandle(responseInterceptors)
  if(open_cancel_model) { // 开启cancel模块后才去走拦截器增强
    [requestInterceptors, responseInterceptors] = $ct.enhanceInterceptor(requestInterceptors, responseInterceptors) // 二次封装拦截器（不管原拦截器有没有提供）
  }
  if(cacheList.length) { // 如果开启了cache模块
    responseInterceptors = $cache.enhanceResponseInterceptor(responseInterceptors) // 扩展响应拦截器（接入缓存功能）
  }
  requestInterceptors && $axios.interceptors.request.use(...requestInterceptors)
  responseInterceptors && $axios.interceptors.response.use(...responseInterceptors)


  // axios.<方法名>中需要代理做预处理的方法名（目前只有get方法，做预处理缓存）
  // 目的：不用每次访问内部方法都重新创建一个代理对象，防止内存泄漏
  let _axiosProxyMap = {}
  // 如果开启异步优化，创建对应方法名代理，全部走优化逻辑
  if(open_async_optimize) {
    async_optimize_method_list.forEach(method => {
      _axiosProxyMap[method] = new Proxy($axios[method], {
        apply(target, thisArg, argumentsList) {
          return to(target(...argumentsList))
        }
      })
    })
  }
  if(cacheList.length) { // 开启缓存模块才应用代理
    _axiosProxyMap = {
      ..._axiosProxyMap,
      get: new Proxy($axios.get, {
        apply(target, thisArg, argumentsList) {
          let [url, config] = argumentsList
          config._path = url
          const data = $cache.getCache(config)
          // 拿到缓存就直接返回数据缓存，拿不到就继续走获取接口
          if(open_async_optimize) {
            return to(data ? Promise.resolve(data) : target(...argumentsList))
          }
          return data ? Promise.resolve(data) : target(...argumentsList);
        },
      })
    }
  }
  const _axiosProxyMapKeys = Object.keys(_axiosProxyMap)


  AxiosProxy = new Proxy($axios, {
    apply(target, thisArg, argumentsList) {
      try {
        let path, config // path(url路径参数), config(用户手动传入的配置)
        // 兼容两种直接调用模式(axios(<url>,<config>) | axios(<config>))
        if(typeof argumentsList[0] === 'string' && isObject(argumentsList[1])) {
          path = argumentsList[0]
          config = argumentsList[1]
          // config.url = path
        } else if(isObject(argumentsList[0])) {
          config = argumentsList[0]
          path = config.url
        }
        // 如果访问的方法类型是要走代理的，就直接访问代理的方法，比如：
        // AxiosProxy('/api/getuser',{method: 'get', params: {}}) -> $axios.get('/api/getuser', {method: 'get', params: {}})
        if(_axiosProxyMapKeys.includes(config.method)) {
          return _axiosProxyMap[config.method](path, config)
        }
      } catch(e) {
      } // 如果乱传值导致中间报错，则直接走下面的return逻辑
      return target(...argumentsList); // 调用原始函数

      /**
       * 如果开启了重试机制（伪代码）
       */ 
      let retryTimes = re_try_times // 本轮重试次数
      return new Promise((resolve, reject) => {
        target(...argumentsList).then((response) => {
          // 在响应钩子里面对请求异常（非网络错误）的也进行throw操作，会走到下面的catch中统一处理重试逻辑，否则直接resolve(response)
        }).catch((error) => {
          // 重试次数没有结束就立即重试（包括网络错误和请求异常两种情况，都在这统一处理）
          // 重试次数用完，则直接throw，会链式抛出异常给用户自定义外层promise处理
        })
      })
    },
    get(target, prop, receiver) { // 如果属性被代理就走代理，不然就正常返回（不用care的变量类型）
      return _axiosProxyMapKeys.includes(prop) ? _axiosProxyMap[prop] : Reflect.get(target, prop, receiver);
    }
  })
  return AxiosProxy
}